/**
 * 
 */
package comp6231.project.testcases;


import java.util.ArrayList;

import org.omg.CORBA.ORB;
import org.omg.CosNaming.Binding;
import org.omg.CosNaming.BindingIteratorHolder;
import org.omg.CosNaming.BindingListHolder;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper;

import comp6231.project.frontend.RetailStoreFrontEndInterface;
import comp6231.project.frontend.RetailStoreFrontEndInterfaceHelper;

/**
 * @author dimitri.tiago
 *
 */
public class CustomerClient implements Runnable
{
	private final int REGISTRY_PORT     = 1050;
	private final String STORE_REGISTRY = String.format("!!localhost:%d!", REGISTRY_PORT);
	
	private String customerID;
	private int itemID;
	private int numberOfItems;
	private String operation;
	private int desiredItemID;
	private int desiredNumber;
	
	ORB orb;
	NamingContextExt namingContextExt;
	
	public CustomerClient(String customerID, String operation, int itemID, int numberOfItems)
	{
		this.customerID    = customerID;
		this.operation	   = operation;
		this.itemID        = itemID;
		this.numberOfItems = numberOfItems;
	
		try 
		{
			// obtain a new ORB instance
			String [] orbSettings = {"-ORBInitialHost", "localhost", "-ORBInitialPort", String.valueOf(REGISTRY_PORT)};
			orb = ORB.init(orbSettings, null);
			
			// obtain the root naming context
			org.omg.CORBA.Object namingContext = orb.resolve_initial_references("NameService"); // obtain the naming context CORBA object
			namingContextExt  = (NamingContextExt) NamingContextExtHelper.narrow(namingContext); // obtain java naming context extension
		} 
		catch (Exception e) 
		{
			e.printStackTrace();
		}
	}
	
	public CustomerClient(String customerID, String operation, int boughtItemID, int boughtNumber, int desiredItemID, int desiredNumber)
	{
		this.customerID    = customerID;
		this.operation	   = operation;
		this.itemID        = boughtItemID;
		this.numberOfItems = boughtNumber;
		this.desiredItemID = desiredItemID;
		this.desiredNumber = desiredNumber;
		
		try 
		{
			// obtain a new ORB instance
			String [] orbSettings = {"-ORBInitialHost", "localhost", "-ORBInitialPort", String.valueOf(REGISTRY_PORT)};
			orb = ORB.init(orbSettings, null);
			
			// obtain the root naming context
			org.omg.CORBA.Object namingContext = orb.resolve_initial_references("NameService"); // obtain the naming context CORBA object
			namingContextExt  = (NamingContextExt) NamingContextExtHelper.narrow(namingContext); // obtain java naming context extension
		} 
		catch (Exception e) 
		{
			e.printStackTrace();
		}
	}
	
	@Override
	public void run() 
	{   
	    if (operation.equalsIgnoreCase("B"))
		   {
			   // buy item(s)
			   String transactionResult = Buy(itemID, numberOfItems); // buy items
			   System.out.printf("%s\n", transactionResult);
		   }
		   else if (operation.equalsIgnoreCase("R"))
		   {
			   // return item(s)
			   String transactionResult = returnItems(itemID, numberOfItems); // return items
			   System.out.printf("%s\n", transactionResult);
		   }
		   else if (operation.equalsIgnoreCase("C"))
		   {
			   // check stock
			   String transactionResult = checkStock(itemID); // check stock
			   System.out.printf("%s\n", transactionResult);
		   }
		   else if (operation.equalsIgnoreCase("E"))
		   {
			   String transactionResult = exchangeItems(itemID, numberOfItems, desiredItemID, desiredNumber); // exchange items
			   System.out.printf("%s\n", transactionResult);
		   } 
	    
	}
	
	public String Buy(int itemID, int numberOfItems)
	{
		int itemsBought 		  = 0;
		String transactionResult  = null;

		String closestRetailStore = getClosestRetailStore();
		
		try 
		{
			RetailStoreFrontEndInterface retailStore = RetailStoreFrontEndInterfaceHelper.narrow(namingContextExt.resolve_str(closestRetailStore));
			itemsBought = retailStore.buyItems(customerID, itemID, numberOfItems);
			transactionResult = (itemsBought == 0)? 
				String.format("%s > %s", customerID, "There were not enough items at retail store to satisfy request.") :	
				String.format("%s > Bought %d items at retail store.", customerID, itemsBought);
		} 
		catch (Exception e)
		{
			e.printStackTrace();
		}
		
		return transactionResult;
	}

	public String returnItems(int itemID, int numberOfItems)
	{
		int itemsReturned 		  = 0;
		String transactionResult  = null;

		String closestRetailStore = getClosestRetailStore();
		
		try 
		{
			RetailStoreFrontEndInterface retailStore = RetailStoreFrontEndInterfaceHelper.narrow(namingContextExt.resolve_str(closestRetailStore));
			itemsReturned = retailStore.returnItems(customerID, itemID, numberOfItems);
			transactionResult = (itemsReturned == 0)? 
				String.format("%s > %s", customerID, "Return of items rejected. Returning more items than bought.") :	
				String.format("%s > Returned %d items to retail store.", customerID, itemsReturned);
		} 
		catch (Exception e)
		{
			e.printStackTrace();
		}
		
		return transactionResult;
	}	
	
	public String checkStock(int itemID)
	{
		int itemsInStock 		  = 0;
		String transactionResult  = null;

		String closestRetailStore = getClosestRetailStore();
		
		try 
		{
			RetailStoreFrontEndInterface retailStore = RetailStoreFrontEndInterfaceHelper.narrow(namingContextExt.resolve_str(closestRetailStore));
			itemsInStock = retailStore.checkStock(itemID);
			transactionResult =	String.format("%s > There are %d items in stock for ItemID %s", customerID, itemsInStock, itemID);	
		} 
		catch (Exception e)
		{
			e.printStackTrace();
		}
		
		return transactionResult;
	}
	
	public String exchangeItems(int boughtItemID, int boughtNumber, int desiredItemID, int desiredNumber)
	{
		int exchangedItems 		  = 0;
		String transactionResult  = null;

		try 
		{
			String closestRetailStore = getClosestRetailStore();
			RetailStoreFrontEndInterface retailStore = RetailStoreFrontEndInterfaceHelper.narrow(namingContextExt.resolve_str(closestRetailStore));
			exchangedItems = retailStore.exchangeItems(customerID, boughtItemID, boughtNumber, desiredItemID, desiredNumber);
			transactionResult = (exchangedItems == 0)? 
					String.format("%s", "Exchange request rejected.") :
					String.format("Exchanged %d of ItemID %d for %d of ItemID % d.", boughtNumber, boughtItemID, desiredNumber, desiredItemID);
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		
		return transactionResult;
	}
	
	/**
	 * This method returns the closes retail store to the customer. 
	 * This is determined through a match between the 1st character of
	 * the customer id and the 1st character of the store name 
	 * (i.e. remote object name in the registry).
	 * @return Returns closes store URL name (i.e. remote object name)
	 */
	private String getClosestRetailStore()
	{
		String closestStore = null;
		
		// obtain code indicating closest store to customer
		String customerLocation	= customerID.substring(0, 1);
		
		// obtain store registry names for retail stores
		ArrayList<String> registeredStores = new ArrayList<String>();
		
		try
		{  
			BindingListHolder bindingListHolder 		= new BindingListHolder(); // obtain names of bindings
			BindingIteratorHolder bindingIteratorHolder = new BindingIteratorHolder();
			namingContextExt.list(1000, bindingListHolder, bindingIteratorHolder);
			
			Binding bindings [] = bindingListHolder.value;	// obtain closest store
			for (int i=0; i < bindings.length; i++) 
			{
	            int lastIx = bindings[i].binding_name.length-1;
	            registeredStores.add(bindings[i].binding_name[lastIx].id);
	        }
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		
		// determine closest store to customer
		String [] retailStores = registeredStores.toArray(new String [0]);
		for (String store : retailStores)
		{
			String storeLocation = ((store.split("!"))[3]).substring(0, 1);	// obtain first character of store name (indicates location)
			if (storeLocation.equalsIgnoreCase(customerLocation))
			{
				// location match indicates closest store
				return store;
			}
		}
				
		return closestStore;
	}
}
